package com.bupt.hotel.hvac.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.bupt.hotel.hvac.common.Constants;
import com.bupt.hotel.hvac.config.HvacConfig;
import com.bupt.hotel.hvac.mapper.DetailRecordMapper;
import com.bupt.hotel.hvac.mapper.RoomStateMapper;
import com.bupt.hotel.hvac.model.entity.DetailRecord;
import com.bupt.hotel.hvac.model.entity.RoomState;
import com.bupt.hotel.hvac.model.enums.ACMode;
import com.bupt.hotel.hvac.model.enums.FanSpeed;
import com.bupt.hotel.hvac.model.enums.RoomStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

/**
 * 空调服务
 * 负责处理客房的温控请求，计算温度变化及费用
 */
@Slf4j
@Service
public class ACService {

    private final RoomStateMapper roomStateMapper;
    private final DetailRecordMapper detailRecordMapper;
    private final HvacConfig config;

    /**
     * 内存中的房间状态缓存（提高性能）
     */
    private final Map<String, RoomState> roomCache = new ConcurrentHashMap<>();

    /**
     * 温度更新定时器
     */
    private final Map<String, Timer> tempTimers = new ConcurrentHashMap<>();

    /**
     * 状态变化回调
     */
    private final Map<String, Consumer<RoomState>> callbacks = new ConcurrentHashMap<>();

    public ACService(RoomStateMapper roomStateMapper, DetailRecordMapper detailRecordMapper, HvacConfig config) {
        this.roomStateMapper = roomStateMapper;
        this.detailRecordMapper = detailRecordMapper;
        this.config = config;
        log.info("空调服务初始化完成");
    }

    /**
     * 初始化房间
     */
    public RoomState initRoom(String roomId, ACMode mode) {
        // 先检查数据库是否已存在
        RoomState existing = roomStateMapper.selectOne(
                new LambdaQueryWrapper<RoomState>()
                        .eq(RoomState::getRoomId, roomId));

        if (existing != null) {
            // 重置房间状态
            existing.setIsOn(false);
            existing.setMode(mode);
            existing.setCurrentTemp(Constants.INITIAL_TEMP.get(mode));
            existing.setInitialTemp(Constants.INITIAL_TEMP.get(mode));
            existing.setTargetTemp(Constants.DEFAULT_TEMP);
            existing.setFanSpeed(FanSpeed.MEDIUM);
            existing.setStatus(RoomStatus.OFF);
            existing.setTotalCost(BigDecimal.ZERO);
            existing.setTotalPowerConsumption(BigDecimal.ZERO);
            existing.setLastUpdateTime(LocalDateTime.now());
            existing.setServiceStartTime(null);
            existing.setUpdateTime(LocalDateTime.now());

            roomStateMapper.updateById(existing);
            roomCache.put(roomId, existing);
            log.info("重置房间 {} 状态，模式: {}", roomId, mode);
            return existing;
        }

        // 创建新房间
        BigDecimal initialTemp = Constants.INITIAL_TEMP.get(mode);
        RoomState roomState = new RoomState();
        roomState.setRoomId(roomId);
        roomState.setIsOn(false);
        roomState.setMode(mode);
        roomState.setCurrentTemp(initialTemp);
        roomState.setInitialTemp(initialTemp);
        roomState.setTargetTemp(Constants.DEFAULT_TEMP);
        roomState.setFanSpeed(FanSpeed.MEDIUM);
        roomState.setStatus(RoomStatus.OFF);
        roomState.setTotalCost(BigDecimal.ZERO);
        roomState.setTotalPowerConsumption(BigDecimal.ZERO);
        roomState.setLastUpdateTime(LocalDateTime.now());
        roomState.setServiceStartTime(null);
        roomState.setCreateTime(LocalDateTime.now());
        roomState.setUpdateTime(LocalDateTime.now());

        roomStateMapper.insert(roomState);
        roomCache.put(roomId, roomState);

        log.info("初始化房间 {} 成功，模式: {}, 初始温度: {}°C", roomId, mode, initialTemp);
        return roomState;
    }

    /**
     * 开机
     */
    public RoomState turnOn(String roomId) {
        RoomState room = getRoomFromCache(roomId);
        if (room == null) {
            log.error("房间 {} 不存在", roomId);
            return null;
        }

        LocalDateTime now = LocalDateTime.now();
        room.setIsOn(true);
        room.setStatus(RoomStatus.STANDBY);
        room.setServiceStartTime(now);
        room.setLastUpdateTime(now);
        room.setUpdateTime(now);

        updateRoomState(room);

        // 添加详单记录
        addDetailRecord(roomId, null, "开机", null, null,
                room.getCurrentTemp(), BigDecimal.ZERO, BigDecimal.ZERO, 0);

        // 停止关机状态下的温度变化
        stopTempUpdate(roomId);

        // 触发回调
        triggerCallback(roomId, room);

        log.info("房间 {} 开机成功", roomId);
        return room;
    }

    /**
     * 关机
     */
    public RoomState turnOff(String roomId) {
        RoomState room = getRoomFromCache(roomId);
        if (room == null) {
            return null;
        }

        // 停止送风
        stopServing(roomId);

        room.setIsOn(false);
        room.setStatus(RoomStatus.OFF);
        room.setLastUpdateTime(LocalDateTime.now());
        // 不清空 serviceStartTime，保留最后一次的服务开始时间
        room.setUpdateTime(LocalDateTime.now());

        updateRoomState(room);

        // 添加详单记录
        addDetailRecord(roomId, null, "关机", null, null,
                room.getCurrentTemp(), BigDecimal.ZERO, BigDecimal.ZERO, 0);

        // 启动关机状态下的温度变化
        startOffTempUpdate(roomId);

        // 触发回调
        triggerCallback(roomId, room);

        log.info("房间 {} 关机成功", roomId);
        return room;
    }

    /**
     * 设置目标温度
     */
    public RoomState setTargetTemp(String roomId, BigDecimal targetTemp) {
        RoomState room = getRoomFromCache(roomId);
        if (room == null || !room.getIsOn()) {
            return null;
        }

        BigDecimal oldTemp = room.getTargetTemp();
        room.setTargetTemp(targetTemp);
        room.setLastUpdateTime(LocalDateTime.now());
        room.setUpdateTime(LocalDateTime.now());

        updateRoomState(room);

        // 添加详单记录
        String action = String.format("调温: %.1f°C → %.1f°C", oldTemp, targetTemp);
        addDetailRecord(roomId, null, action, null, targetTemp,
                room.getCurrentTemp(), BigDecimal.ZERO, BigDecimal.ZERO, 0);

        // 触发回调
        triggerCallback(roomId, room);

        log.debug("房间 {} 设置目标温度: {}°C", roomId, targetTemp);
        return room;
    }

    /**
     * 设置风速
     */
    public RoomState setFanSpeed(String roomId, FanSpeed fanSpeed) {
        RoomState room = getRoomFromCache(roomId);
        if (room == null || !room.getIsOn()) {
            return null;
        }

        FanSpeed oldSpeed = room.getFanSpeed();
        room.setFanSpeed(fanSpeed);
        room.setLastUpdateTime(LocalDateTime.now());
        room.setUpdateTime(LocalDateTime.now());

        updateRoomState(room);

        // 添加详单记录
        String action = String.format("调风: %s → %s", oldSpeed.getDescription(), fanSpeed.getDescription());
        addDetailRecord(roomId, null, action, fanSpeed, null,
                room.getCurrentTemp(), BigDecimal.ZERO, BigDecimal.ZERO, 0);

        // 触发回调
        triggerCallback(roomId, room);

        log.debug("房间 {} 设置风速: {}", roomId, fanSpeed);
        return room;
    }

    /**
     * 设置工作模式
     */
    public RoomState setMode(String roomId, ACMode mode) {
        RoomState room = getRoomFromCache(roomId);
        if (room == null || !room.getIsOn()) {
            return null;
        }

        ACMode oldMode = room.getMode();
        room.setMode(mode);
        room.setLastUpdateTime(LocalDateTime.now());
        room.setUpdateTime(LocalDateTime.now());

        updateRoomState(room);

        // 添加详单记录
        String action = String.format("切换模式: %s → %s",
                oldMode.getDescription(), mode.getDescription());
        addDetailRecord(roomId, null, action, null, null,
                room.getCurrentTemp(), BigDecimal.ZERO, BigDecimal.ZERO, 0);

        // 触发回调
        triggerCallback(roomId, room);

        log.info("房间 {} 切换模式: {}", roomId, mode);
        return room;
    }

    /**
     * 开始送风服务
     */
    public RoomState startServing(String roomId) {
        RoomState room = getRoomFromCache(roomId);
        if (room == null || !room.getIsOn()) {
            return null;
        }

        room.setStatus(RoomStatus.SERVING);
        room.setServiceStartTime(LocalDateTime.now());
        room.setLastUpdateTime(LocalDateTime.now());
        room.setUpdateTime(LocalDateTime.now());

        updateRoomState(room);

        // 添加详单记录
        String action = String.format("开始送风 (%s)", room.getFanSpeed().getDescription());
        addDetailRecord(roomId, null, action, room.getFanSpeed(), null,
                room.getCurrentTemp(), BigDecimal.ZERO, BigDecimal.ZERO, 0);

        // 启动温度更新定时器
        startTempUpdate(roomId);

        // 触发回调
        triggerCallback(roomId, room);

        log.info("房间 {} 开始送风，风速: {}", roomId, room.getFanSpeed());
        return room;
    }

    /**
     * 停止送风服务
     */
    public RoomState stopServing(String roomId) {
        RoomState room = getRoomFromCache(roomId);
        if (room == null) {
            return null;
        }

        boolean wasServing = room.getStatus() == RoomStatus.SERVING ||
                room.getStatus() == RoomStatus.TARGET_REACHED;

        room.setStatus(room.getIsOn() ? RoomStatus.STANDBY : RoomStatus.OFF);

        if (wasServing && room.getServiceStartTime() != null) {
            long now = System.currentTimeMillis();
            long startTime = room.getServiceStartTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
            int duration = (int) ((now - startTime) / 1000);

            BigDecimal powerConsumption = calculatePowerConsumption(room.getFanSpeed(), duration);
            BigDecimal cost = powerConsumption.multiply(Constants.BILLING_RATE);

            // 添加详单记录
            addDetailRecord(roomId, null, "停止送风", null, null,
                    room.getCurrentTemp(), powerConsumption, cost, duration);
        }

        room.setServiceStartTime(null);
        room.setLastUpdateTime(LocalDateTime.now());
        room.setUpdateTime(LocalDateTime.now());

        updateRoomState(room);

        // 停止温度更新定时器
        stopTempUpdate(roomId);

        log.info("房间 {} 停止送风", roomId);
        return room;
    }

    /**
     * 设置为等待状态
     */
    public RoomState setWaiting(String roomId) {
        RoomState room = getRoomFromCache(roomId);
        if (room == null) {
            return null;
        }

        // 如果正在送风，先停止送风
        if (room.getStatus() == RoomStatus.SERVING) {
            stopServing(roomId);
        }

        room.setStatus(RoomStatus.WAITING);
        room.setLastUpdateTime(LocalDateTime.now());
        room.setUpdateTime(LocalDateTime.now());

        updateRoomState(room);

        // 添加详单记录
        addDetailRecord(roomId, null, "进入等待队列", null, null,
                room.getCurrentTemp(), BigDecimal.ZERO, BigDecimal.ZERO, 0);

        // 触发回调
        triggerCallback(roomId, room);

        log.debug("房间 {} 进入等待队列", roomId);
        return room;
    }

    /**
     * 设置为待机状态
     */
    public RoomState setStandby(String roomId) {
        RoomState room = getRoomFromCache(roomId);
        if (room == null || !room.getIsOn()) {
            return null;
        }

        room.setStatus(RoomStatus.STANDBY);
        room.setLastUpdateTime(LocalDateTime.now());
        room.setUpdateTime(LocalDateTime.now());

        updateRoomState(room);

        // 添加详单记录
        addDetailRecord(roomId, null, "退出等待，进入待机", null, null,
                room.getCurrentTemp(), BigDecimal.ZERO, BigDecimal.ZERO, 0);

        // 触发回调
        triggerCallback(roomId, room);

        return room;
    }

    /**
     * 启动温度更新定时器（送风中）
     */
    private void startTempUpdate(String roomId) {
        stopTempUpdate(roomId);

        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                updateTemperature(roomId);
            }
        }, config.getTempUpdateInterval(), config.getTempUpdateInterval());

        tempTimers.put(roomId, timer);
    }

    /**
     * 启动关机状态下的温度更新
     */
    private void startOffTempUpdate(String roomId) {
        stopTempUpdate(roomId);

        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                RoomState room = getRoomFromCache(roomId);
                if (room == null || room.getIsOn()) {
                    stopTempUpdate(roomId);
                    return;
                }

                // 关机状态下温度向初始温度变化
                BigDecimal targetTemp = room.getInitialTemp();
                BigDecimal currentTemp = room.getCurrentTemp();

                if (currentTemp.subtract(targetTemp).abs().compareTo(new BigDecimal("0.1")) < 0) {
                    // 已经接近初始温度，停止变化
                    room.setCurrentTemp(targetTemp);
                    updateRoomState(room);
                    stopTempUpdate(roomId);
                    return;
                }

                // 计算温度变化
                BigDecimal changeRate = Constants.TEMP_CHANGE_RATE_OFF.divide(new BigDecimal("60"), 6,
                        RoundingMode.HALF_UP);
                int direction = targetTemp.compareTo(currentTemp) > 0 ? 1 : -1;
                BigDecimal newTemp = currentTemp.add(changeRate.multiply(new BigDecimal(direction)));

                room.setCurrentTemp(newTemp);
                room.setLastUpdateTime(LocalDateTime.now());
                room.setUpdateTime(LocalDateTime.now());
                updateRoomState(room);

                triggerCallback(roomId, room);
            }
        }, config.getTempUpdateInterval(), config.getTempUpdateInterval());

        tempTimers.put(roomId, timer);
    }

    /**
     * 停止温度更新
     */
    private void stopTempUpdate(String roomId) {
        Timer timer = tempTimers.remove(roomId);
        if (timer != null) {
            timer.cancel();
        }
    }

    /**
     * 更新温度
     */
    private void updateTemperature(String roomId) {
        RoomState room = getRoomFromCache(roomId);
        if (room == null || (room.getStatus() != RoomStatus.SERVING &&
                room.getStatus() != RoomStatus.TARGET_REACHED)) {
            return;
        }

        BigDecimal currentTemp = room.getCurrentTemp();
        BigDecimal targetTemp = room.getTargetTemp();
        FanSpeed fanSpeed = room.getFanSpeed();
        ACMode mode = room.getMode();

        // 实时累加费用（仅SERVING状态）
        if (room.getStatus() == RoomStatus.SERVING && room.getServiceStartTime() != null) {
            long now = System.currentTimeMillis();
            long lastTime = room.getLastUpdateTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
            int incrementSeconds = (int) ((now - lastTime) / 1000);

            if (incrementSeconds > 0) {
                BigDecimal incrementPower = calculatePowerConsumption(fanSpeed, incrementSeconds);
                BigDecimal incrementCost = incrementPower.multiply(Constants.BILLING_RATE);

                room.setTotalPowerConsumption(room.getTotalPowerConsumption().add(incrementPower));
                room.setTotalCost(room.getTotalCost().add(incrementCost));
            }
        }

        // 检查是否已达到目标温度
        if (room.getStatus() == RoomStatus.SERVING &&
                currentTemp.subtract(targetTemp).abs().compareTo(new BigDecimal("0.1")) < 0) {
            room.setCurrentTemp(targetTemp);
            room.setStatus(RoomStatus.TARGET_REACHED);
            room.setLastUpdateTime(LocalDateTime.now());
            room.setUpdateTime(LocalDateTime.now());

            updateRoomState(room);

            // 添加详单记录
            addDetailRecord(roomId, null, "达到目标温度，停止送风", null, null,
                    room.getCurrentTemp(), BigDecimal.ZERO, BigDecimal.ZERO, 0);

            triggerCallback(roomId, room);
            log.info("房间 {} 达到目标温度 {}°C", roomId, targetTemp);
            return;
        }

        // TARGET_REACHED状态下监控温度变化（环境影响）
        if (room.getStatus() == RoomStatus.TARGET_REACHED) {
            BigDecimal envChangeRate = Constants.TEMP_CHANGE_RATE_OFF.divide(new BigDecimal("60"), 6,
                    RoundingMode.HALF_UP);
            int direction = mode == ACMode.COOLING ? 1 : -1;
            BigDecimal newTemp = currentTemp.add(envChangeRate.multiply(new BigDecimal(direction)));

            room.setCurrentTemp(newTemp);
            room.setLastUpdateTime(LocalDateTime.now());
            room.setUpdateTime(LocalDateTime.now());
            updateRoomState(room);

            triggerCallback(roomId, room);
            return;
        }

        // 计算温度变化（SERVING状态）
        BigDecimal changeRate = Constants.TEMP_CHANGE_RATE.get(fanSpeed)
                .divide(new BigDecimal("60"), 6, RoundingMode.HALF_UP);
        int direction = mode == ACMode.COOLING ? -1 : 1;

        // 确保不会越过目标温度
        BigDecimal tempDiff = targetTemp.subtract(currentTemp).abs();
        BigDecimal actualChange = changeRate.min(tempDiff);

        BigDecimal newTemp = currentTemp.add(actualChange.multiply(new BigDecimal(direction)));
        room.setCurrentTemp(newTemp);
        room.setLastUpdateTime(LocalDateTime.now());
        room.setUpdateTime(LocalDateTime.now());

        updateRoomState(room);
        triggerCallback(roomId, room);
    }

    /**
     * 检查是否需要重新启动
     */
    public boolean checkRestart(String roomId) {
        RoomState room = getRoomFromCache(roomId);
        if (room == null || !room.getIsOn()) {
            return false;
        }

        // 只有待机或达到目标温度状态才检查
        if (room.getStatus() != RoomStatus.STANDBY && room.getStatus() != RoomStatus.TARGET_REACHED) {
            return false;
        }

        BigDecimal tempDiff = room.getCurrentTemp().subtract(room.getTargetTemp()).abs();
        return tempDiff.compareTo(Constants.TEMP_DEVIATION_THRESHOLD) > 0;
    }

    /**
     * 计算耗电量
     */
    private BigDecimal calculatePowerConsumption(FanSpeed fanSpeed, int durationSeconds) {
        BigDecimal ratePerMinute = Constants.POWER_CONSUMPTION_RATE.get(fanSpeed);
        BigDecimal durationMinutes = new BigDecimal(durationSeconds).divide(new BigDecimal("60"), 6,
                RoundingMode.HALF_UP);
        return ratePerMinute.multiply(durationMinutes).setScale(6, RoundingMode.HALF_UP);
    }

    /**
     * 获取当前费用信息
     */
    public Map<String, Object> getCurrentCost(String roomId) {
        RoomState room = getRoomFromCache(roomId);
        Map<String, Object> result = new HashMap<>();

        if (room == null) {
            result.put("powerConsumption", BigDecimal.ZERO);
            result.put("cost", BigDecimal.ZERO);
            result.put("duration", 0);
            return result;
        }

        if (room.getServiceStartTime() != null &&
                (room.getStatus() == RoomStatus.SERVING || room.getStatus() == RoomStatus.TARGET_REACHED)) {
            long now = System.currentTimeMillis();
            long startTime = room.getServiceStartTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
            int duration = (int) ((now - startTime) / 1000);

            BigDecimal powerConsumption = calculatePowerConsumption(room.getFanSpeed(), duration);
            BigDecimal cost = powerConsumption.multiply(Constants.BILLING_RATE);

            result.put("powerConsumption", powerConsumption);
            result.put("cost", cost);
            result.put("duration", duration);
            return result;
        }

        result.put("powerConsumption", BigDecimal.ZERO);
        result.put("cost", BigDecimal.ZERO);
        result.put("duration", 0);
        return result;
    }

    /**
     * 添加详单记录
     */
    private void addDetailRecord(String roomId, Long billId, String action,
            FanSpeed fanSpeed, BigDecimal targetTemp,
            BigDecimal currentTemp, BigDecimal powerConsumption,
            BigDecimal cost, Integer duration) {
        DetailRecord record = new DetailRecord();
        record.setRoomId(roomId);
        record.setBillId(billId);
        record.setTimestamp(LocalDateTime.now());
        record.setAction(action);
        record.setFanSpeed(fanSpeed);
        record.setTargetTemp(targetTemp);
        record.setCurrentTemp(currentTemp);
        record.setPowerConsumption(powerConsumption);
        record.setCost(cost);
        record.setDuration(duration);
        record.setCreateTime(LocalDateTime.now());

        detailRecordMapper.insert(record);
    }

    /**
     * 注册状态变化回调
     */
    public void onStateChange(String roomId, Consumer<RoomState> callback) {
        callbacks.put(roomId, callback);
    }

    /**
     * 触发回调
     */
    private void triggerCallback(String roomId, RoomState room) {
        Consumer<RoomState> callback = callbacks.get(roomId);
        if (callback != null) {
            callback.accept(room);
        }
    }

    /**
     * 获取房间状态
     */
    public RoomState getRoomState(String roomId) {
        return getRoomFromCache(roomId);
    }

    /**
     * 获取所有房间状态
     */
    public List<RoomState> getAllRoomStates() {
        return new ArrayList<>(roomCache.values());
    }

    /**
     * 从缓存获取房间（如果缓存中没有，从数据库加载）
     */
    private RoomState getRoomFromCache(String roomId) {
        RoomState room = roomCache.get(roomId);
        if (room == null) {
            room = roomStateMapper.selectOne(
                    new LambdaQueryWrapper<RoomState>()
                            .eq(RoomState::getRoomId, roomId));
            if (room != null) {
                // 修复可能的 null 值（兼容旧数据）
                if (room.getFanSpeed() == null) {
                    room.setFanSpeed(FanSpeed.MEDIUM);
                    updateRoomState(room);
                    log.warn("房间 {} 的 fanSpeed 为 null，已设置默认值 MEDIUM", roomId);
                }
                roomCache.put(roomId, room);
            }
        }
        return room;
    }

    /**
     * 更新房间状态（同步到数据库和缓存）
     */
    private void updateRoomState(RoomState room) {
        roomStateMapper.updateById(room);
        roomCache.put(room.getRoomId(), room);
    }

    /**
     * 清理房间（退房时）
     */
    public void cleanupRoom(String roomId) {
        stopTempUpdate(roomId);
        callbacks.remove(roomId);

        // 从数据库删除房间状态
        roomStateMapper.delete(
                new LambdaQueryWrapper<RoomState>()
                        .eq(RoomState::getRoomId, roomId));

        roomCache.remove(roomId);
        log.info("清理房间 {} 完成", roomId);
    }
}
